//---------------------------------------------------------------------------------
// Copyright (c) 2008 VoiceLynx Project
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
//     1. The origin of this software must not be misrepresented; you must not
//        claim that you wrote the original software. If you use this software
//        in a product, an acknowledgment in the product documentation would be
//        appreciated but is not required.
//
//     2. Altered source versions must be plainly marked as such, and must not be
//        misrepresented as being the original software.
//
//     3. This notice may not be removed or altered from any source
//        distribution.
//---------------------------------------------------------------------------------

#include "VLStandAlone.h"

#include "VLDataStream.h"

#define VL_NET_MAX_PEERS (64)

enum PacketType
{
  VL_PT_NONE,
  VL_PT_UPDATE_CHANNELS
};

// =========== Some useful functions that let us memcpy info in and out of packets ======
ENetPacket *createPacket(PacketType packetType, 
                         unsigned char *payload,
                         size_t payloadSize,
                         bool sendReliably)
{
  enet_uint32 flags = 0;

  if (sendReliably)
  {
    flags |= ENET_PACKET_FLAG_RELIABLE;
  }

  ENetPacket *packet = 
    enet_packet_create(0, payloadSize + sizeof(packetType), flags);

  unsigned char *payloadData = packet->data;

  memcpy(payloadData, &packetType, sizeof(packetType));
  payloadData += sizeof(packetType);

  memcpy(payloadData, payload, payloadSize);

  return packet;
}

ENetPacket *createPacket(PacketType packetType, 
                         OutDataStream &payload,
                         bool sendReliably)
{
  ENetPacket *packet = 
    createPacket(packetType, 
    payload.getBytePtr(), 
    payload.getSize(), 
    sendReliably);

  return packet;
}

ENetPacket *createPacket(PacketType packetType,
                         bool sendReliably)
{
  return createPacket(packetType, 0, 0, sendReliably);
}

PacketType getPacketType(ENetPacket &packet)
{
  return * ((PacketType *) packet.data);
}


//the payload can be null in the case of a packet with no payload
std::auto_ptr<InDataStream> getPacketPayload(ENetPacket &packet)
{
  //if we assert here, then this is probably a malformed packet
  assert(packet.dataLength >= sizeof(PacketType));

  if (packet.dataLength == sizeof(PacketType))
  {
    //there is no payload
    return std::auto_ptr<InDataStream>(0);
  }

  unsigned char *payloadData = packet.data;
  payloadData += sizeof(PacketType);

  size_t dataLength = packet.dataLength - sizeof(PacketType);

  //create a buffer the length of the packet payload minus the header packet type
  std::auto_ptr<std::vector<VLByte> > data(
    new std::vector<VLByte>(dataLength));

  //copy the packet, not including the header packet type, into our buffer
  memcpy(&(*data)[0], payloadData, dataLength);

  std::auto_ptr<InDataStream> inStream(new InDataStream(data));

  return inStream;
}

//////////////////////////// NetBase //////////////////////////////

VLNetBase::VLNetBase(VLBaseCore &core)
: mCore(core)
{

}

VLNetBase::~VLNetBase()
{
  if (mHost.get() != 0)
  {
    closeConnections();
  }
}

void VLNetBase::process()
{
  if (mHost.get())
  {
    mHost->process();
    mCore.think();
  }
}

void VLNetBase::onPacketReceived(VLENetHostWrapper &host,
                                 ENetPeer *peer, 
                                 ENetPacket &packet, 
                                 enet_uint8 channel) 
{ 
  PacketType packetType = getPacketType(packet);

  switch (packetType)
  {
  case VL_PT_UPDATE_CHANNELS:
    //  {
    //    //apply the new game state
    //    std::auto_ptr<InPackedDataStream> gameState = getPacketPayload(packet);
    //  }
    //  break;

  default:
    break;
  } 
}

void VLNetBase::onConnect(VLENetHostWrapper &host, ENetPeer *peer)
{
  VL_TRACE("connected");
  mCore.onConnect(peer);
}

void VLNetBase::onDisconnect(VLENetHostWrapper &host, ENetPeer *peer, enet_uint32 reason)
{
  VL_TRACE("disconnected");
  mCore.onDisconnect(peer);
}


void VLNetBase::closeConnections(VLDisconnectReason reason)
{
  mHost->disconnectAll();
}

///////////////////////// NetClient ///////////////////////////////

VLStandaloneClient::VLStandaloneClient()
: VLNetBase(mCore),
  mPort(0),
  mServerPeer(0),
  mIsConnected(false)
{

}

void VLStandaloneClient::connectToServer(std::string &remoteHost, short port)
{
  mRemoteHost = remoteHost;
  mPort = port;

  //start connecting
  mHost = VLENetSystem::getSingleton().createClient(
    mRemoteHost, 
    mPort, 
    this);

  if (mHost.get() != 0)
  {
    onConnect(*mHost.get(), mHost->getWrappedHost()->lastServicedPeer);
  }
}

bool VLStandaloneClient::isConnected()
{
  return mServerPeer != 0;
}

void VLStandaloneClient::onConnect(VLENetHostWrapper &host, ENetPeer *peer)
{
  // When a client connects, we get notified of the connection before that function
  // returns the mHost
  //mHost = host;

  VLNetBase::onConnect(host, peer);

  //we received the connect callback - set some state
  mServerPeer = peer;
  mIsConnected = true;

  //ask for preload data
  //ENetPacket *packet = createPacket(PT_REQUEST_PRELOAD_DATA, true);

  //not using mHost here because it is not initted yet
  //host.sendPacket(peer, packet);
}

void VLStandaloneClient::onDisconnect(VLENetHostWrapper &host, ENetPeer *peer, enet_uint32 reason)
{
  VLNetBase::onDisconnect(host, peer, reason);

  mServerPeer = 0;
  mIsConnected = false;
}

///////////////////////// NetServer ///////////////////////////////

VLStandaloneServer::VLStandaloneServer()
: VLNetBase(mCore),
  mPort(0)
{

}

void VLStandaloneServer::beginListening(short port)
{
  mPort = port;

  mHost = VLENetSystem::getSingleton().createServer(
    mPort, 
    VL_NET_MAX_PEERS, 
    this);
}
